package util

import (
	"math/rand"
	"sync"
	"time"
)

// 数据传递的结构
type DataEvent struct {
	Data interface{}
	Topic string
}

// DataChannel 是一个能接收 DataEvent 的 channel
// channel 用通信来共享内存，而不要用共享内存来通信  进程间通信的数据结构
type DataChannel chan DataEvent

//切片是围绕动态数组的概念构建的，可以按需自动增长和缩小
type DataChannelSlice []DataChannel

// EventBus 存储有关订阅者感兴趣的特定主题的信息
type EventBus struct {
	Subscribes map[string]DataChannelSlice
	Rm sync.RWMutex
}

//
func (eb *EventBus)Subscribe(topic string, ch DataChannel)  {
	eb.Rm.Lock()
	if prev, found := eb.Subscribes[topic]; found{
		eb.Subscribes[topic] = append(prev,ch)
	}else{
		eb.Subscribes[topic] = append([]DataChannel{},ch)
	}
	eb.Rm.Unlock()
}

func (eb *EventBus) Publish(topic string, data interface{})  {
	eb.Rm.RLock()
	if chans, found := eb.Subscribes[topic];found {
		channels := append(DataChannelSlice{},chans...)
		// 在golang里面，使用go这个关键字，后面再跟上一个函数就可以创建一个线程。后面的这个函数可以是已经写好的函数，也可以是一个匿名函数
		go func(data DataEvent,dataChannelSlices DataChannelSlice) {
			for _,ch := range dataChannelSlices {
				ch <- data
			}
		}(DataEvent{Data: data, Topic: topic},channels)
	}
	eb.Rm.RLock()
}

var eb = &EventBus{
	Subscribes: map[string]DataChannelSlice{},
}

func GetIns() *EventBus {
	if eb == nil {
		eb = &EventBus{
			Subscribes: map[string]DataChannelSlice{},
		}
	}
	return eb
}

func publishTo(topic string,data string)  {
	for {
		eb.Publish(topic,data)
		time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
	}
}